
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// This file contains the implementation for all the Object standard library.

// Object is both a function and a constructor, so we need to create
// a janus function for that. The syntax is a little ugly if we want
// to stay anonymous (cindent is having a terrible time with it).
Esiply.set_attributes(this, "Object", Esiply.DONT_ENUM);
Object = Esiply.create_janus_function(

// Ecma 15.2.1.1 Object([value])
function(value)
{
    // 1. If value is null, undefined, or not supplied, create
    // a new object exactly as if the object constructor had
    // been called with the same arguments
    if (value == null || value == undefined)
    {
        return new Object(value);
    }

    // 2. Return toObject(value)
    return Esiply.to_object(value);
},

// Ecma 15.2.2.1 new Object([value])
function(value)
{
    // 1. If value is not supplied, go to step 8
    if (value != undefined)
    {
        // 2. If type of value is not Object, go to step 5.
        if (typeof value == "object" || typeof value == "function")
        {
            // 3. If value is an object, simply return it
            // 4. Same thing, but for host objects.
            return value;
        }

        // 5. If type of value is String, return toObject(value)
        // 6. If type of value is Boolean, return toObject(value)
        // 7. If type of value is Number, return toObject(value)
        if (typeof value == "string"
                || typeof value == "boolean"
                || typeof value == "number")
        {
            return Esiply.to_object(value);
        }
    }
    
    // 8. Create a new native ECMAScript object.
    // The [[prototype]] property of the newly constructed object is set
    // to the Object prototype object.
    // The [[class]] property of the newly constructed object is set to
    // "Object"
    // The newly constructed object has no [[value]] property.
    // Return the newly created native object.
    // All that stuff is done when we use the object literal.
    return {};
});

// Ecma 15.2.3.1 Object.prototype
// The initial value of Object.prototype is the Object prototype object
// This property has the attributes {DontEnum, DontDelete, ReadOnly}.
Object.prototype = Esiply.get_prototype({});
Esiply.set_attributes(Object, "prototype",
        Esiply.DONT_ENUM | Esiply.DONT_DELETE | Esiply.READ_ONLY);

// Ecma 15.2.4.1 Object.prototype.constructor
// The initial value of Object.prototype.constructor is the built-in 
// Object constructor
Esiply.set_attributes(Object.prototype, "constructor", Esiply.DONT_ENUM);
Object.prototype.constructor = Object;

// Ecma 15.2.4.2 Object.prototype.toString()
Esiply.set_attributes(Object.prototype, "toString", Esiply.DONT_ENUM);
Object.prototype.toString = function()
{
    // 1. Get the [[class]] property of this object.
    // 2. Compute a string value bo concatenating the three strings
    // "[object ", result(1), and "]".
    // 3. Return result(2)
    return "[object " + Esiply.get_class(this) + "]";
};

// Ecma 15.2.4.3 Object.prototype.toLocaleString()
// This function returns the result of calling toString()
Esiply.set_attributes(Object.prototype, "toLocaleString", Esiply.DONT_ENUM);
Object.prototype.toLocaleString = function()
{
    return this.toString();
};

// Ecma 15.2.4.4 Object.prototype.valueOf()
// The valueOf method returns its this value. If the object is the result
// of calling the Object constructor with a host object, it is
// implementation-defined whether valueOf returns this value or another
// value such as the host object originally passed to the constructor.
Esiply.set_attributes(Object.prototype, "valueOf", Esiply.DONT_ENUM);
Object.prototype.valueOf = function()
{
    return Esiply.get_value(this);
};

// Ecma 15.2.4.5 Object.prototype.hasOwnProperty(V)
// The spec doesn't say this, bug just in case you wanted to know:
// Returns false if the object doesn't have this property, and
// true if it does. The prototype chain is not considered.
Esiply.set_attributes(Object.prototype, "hasOwnProperty", Esiply.DONT_ENUM);
Object.prototype.hasOwnProperty = function(v)
{
    // 1. let o be this object
    // 2. call toString(v)
    // 3. if O doesn't have a property with the name given by result(2),
    // return false
    // 4. Return true.
    return Esiply.object_has(this, String(v));
};

// Ecma 15.2.4.6 Object.prototype.isPrototypeOf(V)
// The spec doesn't say this, but just in case you wanted to know:
// The isPrototypeOf function returns true if the "this" value
// is a prototype (somewhere along the chain) of V
Esiply.set_attributes(Object.prototype, "isPrototypeOf", Esiply.DONT_ENUM);
Object.prototype.isPrototypeOf = function(v)
{
    // 1. Let O be this object
    var o = this;

    // 2. If v is not an object, return false
    // (I'm not sure the precedence of these operators)
    if (!(((typeof v) == "object") || ((typeof v) == "function")))
    {
        return false;
    }

    do
    {

        // 3. Let v be the value of the [[prototype]] property of v
        v = Esiply.get_prototype(v);

        // 4. if v is null, return false
        if (v == null)
        {
            return false;
        }

        // 5. If o and v refer to the same object, return true
        if (v === o)
        {
            return true;
        }

        // 6. Go to step 3
    } while(true);
};

// Ecma 15.2.4.7 Object.prototype.propertyIsEnumerable(v)
// The spec doesn't say this, but just in case you wanted to know:
// Returns true if the object has (as in not looking up the prototype
// chain) a property with name v and that property is enumerable.
Esiply.set_attributes(Object.prototype, "propertyIsEnumerable", Esiply.DONT_ENUM);
Object.prototype.propertyIsEnumerable = function(v)
{
    // 1. Let O be this object
    // 2. Call toString(v)
    // 3. if o doesn't have a property with the name given by result(2),
    // return false
    v = String(v);
    if (!this.hasOwnProperty(v))
    {
        return false;
    }

    // 4. If the property has the attribute DontEnum, return false
    // 5. return true.
    var attrib = Esiply.get_attributes(this, v);
    return !(attrib & Esiply.DONT_ENUM);
};

